﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using Affinity.Flux.Pairings;
using Affinity.Core.Validation;

namespace Affinity.Flux
{
    /// <summary>
    /// The standard implementation of IMatchMaker, pairs inlets and outlets and provides no special functionality.
    /// </summary>
    public class StandardMatchMaker : IMatchMaker
    {
        /// <summary>
        /// Default constructor.
        /// </summary>
        public StandardMatchMaker()
        {
        }

        /// <summary>
        /// Pairs the specified inlet and the specified outlet.
        /// </summary>
        /// <param name="inlet"></param>
        /// <param name="outlet"></param>
        public void Pair(Inlet inlet, Outlet outlet)
        {
            lock (_sync) _pairings[Tuple.Create(outlet, inlet)] = CreatePairing(outlet, inlet);
        }

        /// <summary>
        /// Unpairs the specified inlet from the specified outlet.
        /// </summary>
        /// <param name="inlet"></param>
        /// <param name="outlet"></param>
        public void Unpair(Inlet inlet, Outlet outlet)
        {
            lock (_sync)
            {
                Pairing pairing;
                var tuple = Tuple.Create(outlet, inlet);
                _pairings.TryGetValue(tuple, out pairing);

    #if DEBUG
                Assert.NotNull(pairing, "pairing");
    #endif
                if (pairing != null)
                {
                    _pairings.Remove(tuple);
                    pairing.Dispose();
                }
            }
        }

        /// <summary>
        /// Creates a pairing for the given outlet and inlet.
        /// </summary>
        /// <param name="outlet"></param>
        /// <param name="inlet"></param>
        /// <returns></returns>
        protected virtual Pairing CreatePairing(Outlet outlet, Inlet inlet)
        {
            Pairing pairing = null;

            // In: Method, Out: Event

            if (inlet.Member is MethodInfo && outlet.Member is EventInfo)
            {
                pairing = new EventMethodPairing(outlet, inlet, false);
            }

            // In: Property, Out: Property

            if (inlet.Member is PropertyInfo && outlet.Member is PropertyInfo)
            {
                pairing = new PropertyPropertyPairing(outlet, inlet);
            }

            //TODO? In: Method, Out: Delegate

            //TODO? In: Property, Out: Type

            if (pairing == null) throw new NotImplementedException();

            return pairing;
        }

        private readonly Dictionary<Tuple<Outlet, Inlet>, Pairing> _pairings = new Dictionary<Tuple<Outlet, Inlet>, Pairing>();
        private readonly object _sync = new object();
    }
}
